﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DevExpress.Data.Filtering;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Actions;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.Layout;
using DevExpress.ExpressApp.Model.NodeGenerators;
using DevExpress.ExpressApp.SystemModule;
using DevExpress.ExpressApp.Templates;
using DevExpress.ExpressApp.Utils;
using DevExpress.Persistent.Base;
using DevExpress.Persistent.Validation;
using DevExpress.XtraPivotGrid;
using DevExpress.Utils;
using DevExpress.XtraEditors;
using DevExpress.ExpressApp.PivotGrid.Win;
using aztall.Module.BusinessObjects;
using DevExpress.XtraEditors.Controls;
using aztall.Module.Controllers;
using System.Windows.Forms;
using DevExpress.Xpo;
using static aztall.Module.BusinessObjects.MetricHelper;

namespace aztall.Module.Win.Controllers
{
	// For more typical usage scenarios, be sure to check out https://documentation.devexpress.com/eXpressAppFramework/clsDevExpressExpressAppViewControllertopic.aspx.
	public partial class DW_Controller : ViewController
	{
		const string NullText = "Please select a value";
		const string _PivotLayoutId = "DW_AdvanceInput_PivotLayout";
		DetailView thisView = null;
		IObjectSpace nestedOS;
		#region UI Controls
		CheckedComboBoxEdit customerControl, skuControl;
		LookUpEdit metricControl;
		DateEdit fromDateControl, toDateControl;
		BindingSource bsPivotGrid = new BindingSource();
		CalcEdit valueControl;
		XafPivotGridControl pivotGridControl;
		SingleChoiceAction currentView;
		ParametrizedAction addView;
		SimpleAction removeView;

		SimpleAction DialogCancel;
		SimpleAction DialogOK;

		#endregion
		IList<BaseEdit> ToolTipControls = new List<BaseEdit>();
		string[] ValueActions = new string[] { "PivotAction_AddValue", "PivotAction_SetValue", "PivotAction_DistributeValue", "PivotAction_RemoveValue" };
		#region pivot fields
		PivotFieldItem[] pivotFieldItems = new PivotFieldItem[]
		{
			new PivotFieldItem {Area = PivotArea.RowArea, FieldCaption = "Sku",FieldName = "Sku.DisplayText"},
			new PivotFieldItem {Area = PivotArea.RowArea, FieldCaption = "Customer",FieldName = "Customer.DisplayText"},
			new PivotFieldItem {Area = PivotArea.RowArea, FieldCaption = "Metric",FieldName = "Metric.DisplayText"},

			new PivotFieldItem {Area = PivotArea.FilterArea, FieldCaption = "MonthYear",FieldName = "Date", GroupInterval = PivotGroupInterval.DateMonthYear},
			new PivotFieldItem {Area = PivotArea.FilterArea, FieldCaption = "WeekOfMonth",FieldName = "Date", GroupInterval = PivotGroupInterval.DateWeekOfMonth},

			new PivotFieldItem {Area = PivotArea.FilterArea, FieldCaption = "Date",FieldName = "Date", GroupInterval = PivotGroupInterval.Date},
			new PivotFieldItem {Area = PivotArea.FilterArea, FieldCaption = "QuarterYear",FieldName = "Date", GroupInterval = PivotGroupInterval.DateQuarterYear},
			new PivotFieldItem {Area = PivotArea.FilterArea, FieldCaption = "Year",FieldName = "Date", GroupInterval = PivotGroupInterval.DateYear},
			new PivotFieldItem {Area = PivotArea.FilterArea, FieldCaption = "WeekOfYear",FieldName = "Date", GroupInterval = PivotGroupInterval.DateWeekOfYear},
			new PivotFieldItem {Area = PivotArea.FilterArea, FieldCaption = "DayofWeek",FieldName = "Date", GroupInterval = PivotGroupInterval.DateDayOfWeek},

			new PivotFieldItem {Area = PivotArea.DataArea, FieldCaption = "Value",FieldName = "Value", FormatType = FormatType.Numeric, FormatString = "#,0.00"},
		};
		EventHandler setToolTip = (sender, e) => { var c = sender as BaseEdit; c.ToolTip = (c.EditValue == null) ? string.Empty : c.Text; };
		class dsStruct
		{
			public DateTime Date { get; set; }
			public Customer Customer { get; set; }
			public Sku Sku { get; set; }
			public Metric Metric { get; set; }
			public double Value { get; set; }
			public bool IsDirty { get; set; }
		}

		#endregion
		public DW_Controller()
		{
			InitializeComponent();
			// Target required Views (via the TargetXXX properties) and create their Actions.
			base.TargetViewId = "DW_AdvanceInput";
			base.TargetViewType = ViewType.DetailView;
		}
		protected override void OnActivated()
		{
			base.OnActivated();
			// Perform various tasks depending on the target View.
			if (nestedOS == null)
				//nestedOS = base.ObjectSpace.CreateNestedObjectSpace();
				nestedOS = base.ObjectSpace;


			thisView = (DetailView)View;
			bsPivotGrid.DataSource = InitDataSource();

			foreach (string item in ValueActions)
			{
				SimpleAction action = base.Frame.GetController<PivotActions_Controller>().Actions[item] as SimpleAction;
				action.Execute += SetValue;
			}
			currentView = base.Frame.GetController<PivotActions_Controller>().Actions["PivotAction_CurrentView"] as SingleChoiceAction;
			addView = base.Frame.GetController<PivotActions_Controller>().Actions["PivotAction_AddView"] as ParametrizedAction;
			removeView = base.Frame.GetController<PivotActions_Controller>().Actions["PivotAction_RemoveView"] as SimpleAction;
			currentView.Execute += CurrentView_Execute;
			removeView.Execute += RemoveView_Execute;
			addView.Execute += AddView_Execute;

			DialogOK = base.Frame.GetController<DialogController>().Actions["DialogOK"] as SimpleAction;
			DialogCancel = base.Frame.GetController<DialogController>().Actions["DialogCancel"] as SimpleAction;
			DialogOK.Execute += EndDialog;
			DialogCancel.Execute += EndDialog;
		}
		protected override void OnViewControlsCreated()
		{
			base.OnViewControlsCreated();
			// Access and customize the target View control.
			fromDateControl = thisView.FindItem("FromDate").Control as DateEdit;
			toDateControl = thisView.FindItem("ToDate").Control as DateEdit;
			valueControl = thisView.FindItem("Value").Control as CalcEdit;
			customerControl = thisView.FindItem("Customers").Control as CheckedComboBoxEdit;
			if (customerControl != null)
			{
				customerControl.Properties.DataSource = ObjectSpace.GetObjects<Customer>(new BinaryOperator("Domain.Oid", Env.CurrentDomainOid));
				customerControl.Properties.DisplayMember = "DisplayText";
				customerControl.Properties.ValueMember = "Oid";
				customerControl.Properties.NullValuePrompt = NullText;
				customerControl.Properties.NullValuePromptShowForEmptyValue = true;

				ToolTipControls.Add(customerControl);
			}
			skuControl = thisView.FindItem("Skus").Control as CheckedComboBoxEdit;
			if (skuControl != null)
			{
				skuControl.Properties.DataSource = ObjectSpace.GetObjects<Sku>(new BinaryOperator("Domain.Oid", Env.CurrentDomainOid));
				skuControl.Properties.DisplayMember = "DisplayText";
				skuControl.Properties.ValueMember = "Oid";
				skuControl.Properties.NullValuePrompt = NullText;
				skuControl.Properties.NullValuePromptShowForEmptyValue = true;

				ToolTipControls.Add(skuControl);
			}
			metricControl = thisView.FindItem("Metric").Control as LookUpEdit;
			if (metricControl != null)
			{
				metricControl.Properties.DataSource = MetricHelper.Metrics.Where(c => c.MetricType == MetricType.SalesVolume && c.MetricScope != MetricScope.Derived);
				metricControl.Properties.DisplayMember = "DisplayText";
				metricControl.Properties.ValueMember = "Oid";
				metricControl.Properties.NullValuePrompt = NullText;
				metricControl.Properties.NullValuePromptShowForEmptyValue = true;

				metricControl.Properties.Columns.Add(new LookUpColumnInfo("MetricCode", "Metric Code"));
				metricControl.Properties.Columns.Add(new LookUpColumnInfo("MetricDesc", "Metric Description"));
			}
			currentView.Items.Clear();
			pivotGridControl = thisView.FindItem("PivotGrid").Control as XafPivotGridControl;
			if (pivotGridControl != null)
			{
				foreach (var item in pivotFieldItems)
				{
					var newItem = new PivotGridField(item.FieldName, item.Area);
					newItem.Caption = item.FieldCaption;
					if (item.FormatString != string.Empty)
					{
						newItem.CellFormat.FormatString = item.FormatString;
						newItem.CellFormat.FormatType = DevExpress.Utils.FormatType.Numeric;
					}
					newItem.GroupInterval = item.GroupInterval;

					pivotGridControl.Fields.Add(newItem);
				}
				pivotGridControl.DataSource = bsPivotGrid;
			}
			IObjectSpace ios = base.ObjectSpace;
			IList<ConfigValue> configValues = ios.GetObjects<ConfigValue>(
							CriteriaOperator.Parse("Config.ConfigGroup = ? and Config.ConfigName = ?", ConfigGroup.Internal, _PivotLayoutId)
							);
			ConfigValue defaultView = configValues.Where(p => p.Value.ToUpper() == "Default".ToUpper()).FirstOrDefault();

			if (defaultView == null)    // create Default view
			{
				defaultView = PivotGridHelper.AddView(pivotGridControl, ios, ConfigGroup.Internal, _PivotLayoutId, "Default");
				ios.CommitChanges();
				configValues.Add(defaultView);
			}

			foreach (ConfigValue item in configValues)
			{
				currentView.Items.Add(new ChoiceActionItem(item.Oid.ToString(), item.Value, item));
			}

			addView.Value = defaultView.Value;
			currentView.SelectedItem = currentView.Items.Where(c => c.Id == defaultView.Oid.ToString()).First();
			PivotGridHelper.SetView(pivotGridControl, defaultView);
		}
		protected override void OnDeactivated()
		{
			foreach (string item in ValueActions)
			{
				SimpleAction action = base.Frame.GetController<PivotActions_Controller>().Actions[item] as SimpleAction;
				if (action != null)
					action.Execute -= SetValue;
			}
			currentView.Execute -= CurrentView_Execute;
			removeView.Execute -= RemoveView_Execute;
			addView.Execute -= AddView_Execute;
			DialogOK.Execute -= EndDialog;
			DialogCancel.Execute -= EndDialog;

			foreach (BaseEdit item in ToolTipControls)
				item.EditValueChanged -= setToolTip;
			// Unsubscribe from previously subscribed events and release other references and resources.
			base.OnDeactivated();
		}
		private void SetValue(object sender, SimpleActionExecuteEventArgs e)
		{
			SimpleAction action = sender as SimpleAction;
			switch (action.Id)
			{
				case "PivotAction_AddValue":
					UpdateValue((days, oldValue, newValue) => oldValue + newValue);
					break;
				case "PivotAction_SetValue":
					UpdateValue((days, oldValue, newValue) => newValue);
					break;
				case "PivotAction_DistributeValue":
					UpdateValue((days, oldValue, newValue) => newValue / days);
					break;
				case "PivotAction_RemoveValue":
					RemoveValue();
					break;
			}
		}
		private void UpdateValue(Func<int, double, double, double> calculator)
		{
			IList<Customer> dsCust = customerControl.Properties.DataSource as IList<Customer>;
			IEnumerable<Customer> customers = customerControl.Properties
													.Items
													.Cast<CheckedListBoxItem>()
													.Where(c => c.CheckState == CheckState.Checked)
													.Select(x => dsCust[customerControl.Properties.Items.IndexOf(x)]) ?? Enumerable.Empty<Customer>();
			IList<Sku> dsSku = skuControl.Properties.DataSource as IList<Sku>;
			IEnumerable<Sku> skus = skuControl.Properties
													.Items
													.Cast<CheckedListBoxItem>()
													.Where(c => c.CheckState == CheckState.Checked)
													.Select(x => dsSku[skuControl.Properties.Items.IndexOf(x)]) ?? Enumerable.Empty<Sku>();

			Metric metric = nestedOS.GetObjectByKey<Metric>(metricControl.EditValue);

			DateTime fromDate = fromDateControl.DateTime;
			DateTime toDate = toDateControl.DateTime;
			int days = (int)(toDate - fromDate).TotalDays + 1;

			double newValue = (double)valueControl.Value;

			var q = (from cust in customers.DefaultIfEmpty()
							from sku in skus.DefaultIfEmpty()
							from d in Enumerable.Range(0, (int)(toDate - fromDate).TotalDays + 1)
							select new dsStruct
							{
								Date = fromDate.AddDays(d),
								Customer = cust,
								Sku = sku,
								Metric = metric,
								Value = calculator(days, 0, newValue),
							}).ToList();
			IList<dsStruct> ds = bsPivotGrid.DataSource as IList<dsStruct>;
			var newItems = q.Where(c => !ds.Any(x => x.Customer.Oid == c.Customer.Oid && x.Sku.Oid == c.Sku.Oid && x.Date == c.Date && x.Metric.MetricCode == c.Metric.MetricCode));
			var updateItems = ds.Where(c => q.Any(x => x.Customer.Oid == c.Customer.Oid && x.Sku.Oid == c.Sku.Oid && x.Date == c.Date && x.Metric.MetricCode == c.Metric.MetricCode));

			foreach (dsStruct item in updateItems)
			{
				item.Value = calculator(days, item.Value, newValue);
			}

			foreach (dsStruct item in newItems)
			{
				ds.Add(item);
			}

			pivotGridControl.DataSource = null;
			pivotGridControl.DataSource = bsPivotGrid;
		}
		private void RemoveValue()
		{
			Metric metric = nestedOS.GetObjectByKey<Metric>(metricControl.EditValue);
			if (metric == null)
				return;

			DateTime fromDate = fromDateControl.DateTime;
			DateTime toDate = toDateControl.DateTime;
			int days = (int)(toDate - fromDate).TotalDays + 1;

			if (days <= 0)
				return;

			IList<Customer> dsCust = customerControl.Properties.DataSource as IList<Customer>;
			IEnumerable<Customer> customers = customerControl.Properties
													.Items
													.Cast<CheckedListBoxItem>()
													.Where(c => c.CheckState == CheckState.Checked)
													.Select(x => dsCust[customerControl.Properties.Items.IndexOf(x)]) ?? Enumerable.Empty<Customer>();

			IList<Sku> dsSku = skuControl.Properties.DataSource as IList<Sku>;
			IEnumerable<Sku> skus = skuControl.Properties
													.Items
													.Cast<CheckedListBoxItem>()
													.Where(c => c.CheckState == CheckState.Checked)
													.Select(x => dsSku[skuControl.Properties.Items.IndexOf(x)]) ?? Enumerable.Empty<Sku>();

			IList<dsStruct> ds = bsPivotGrid.DataSource as IList<dsStruct>;

			toDate = ds.Where(c => customers.Contains(c.Customer) && skus.Contains(c.Sku) && c.Date <= toDate).Max(c => c.Date);
			fromDate = ds.Where(c => customers.Contains(c.Customer) && skus.Contains(c.Sku) && c.Date >= fromDate).Min(c => c.Date);

			var d = ds.Where(c => customers.Any(x => x.Oid == c.Customer.Oid) && skus.Any(x => x.Oid == c.Sku.Oid) && c.Metric.MetricCode == metric.MetricCode && c.Date >= fromDate && c.Date <= toDate).ToList();
			foreach (var v in d)
			{
				ds.Remove(v);
			}
			pivotGridControl.RefreshData();
		}
		private void AddView_Execute(object sender, ParametrizedActionExecuteEventArgs e)
		{
			if (e.ParameterCurrentValue == null)
				return;

			string viewName = e.ParameterCurrentValue.ToString();

			IObjectSpace ios = this.Application.CreateObjectSpace();

			ConfigValue configValue = PivotGridHelper.AddView(pivotGridControl, ios, ConfigGroup.Internal, _PivotLayoutId, viewName);

			ios.CommitChanges();

			ChoiceActionItem item = currentView.Items.Where(c => c.Id == configValue.Oid.ToString()).FirstOrDefault();
			if (item == null)
			{
				item = new ChoiceActionItem(configValue.Oid.ToString(), configValue.Value, configValue);
				currentView.Items.Add(item);
			}
			else
			{
				item.Caption = viewName;
				item.Data = configValue;
			}

			currentView.SelectedItem = item;
		}
		private void CurrentView_Execute(object sender, SingleChoiceActionExecuteEventArgs e)
		{
			ConfigValue configValue = e.SelectedChoiceActionItem.Data as ConfigValue;
			addView.Value = configValue.Value;
			PivotGridHelper.SetView(pivotGridControl, configValue);

		}
		private void RemoveView_Execute(object sender, SimpleActionExecuteEventArgs e)
		{
			if (currentView.SelectedItem == null)
				return;

			ConfigValue configValue = currentView.SelectedItem.Data as ConfigValue;
			IObjectSpace ios = this.Application.CreateObjectSpace();
			PivotGridHelper.DeleteView(ios, configValue);
			ios.CommitChanges();

			currentView.Items.Remove(currentView.SelectedItem);
			addView.Value = string.Empty;
		}
		private void EndDialog(object sender, SimpleActionExecuteEventArgs e)
		{
			if (nestedOS == null)
				throw new Exception("PromoCost_Controller.EndDialog: " + nameof(nestedOS) + " is null.");

			SimpleAction result = sender as SimpleAction;
			switch (result.Id)
			{
				case "DialogOK":
					try
					{
						SaveChanges();
						nestedOS.CommitChanges();
					}
					catch
					{
						nestedOS.Rollback();
						throw;
					}
					break;
				case "DialogCancel":
					nestedOS.Rollback();
					break;
			}

			//nestedOS.Dispose();
			//nestedOS = null;

		}
		private List<dsStruct> InitDataSource()
		{
			CollectionSource DWs = thisView.CurrentObject as CollectionSource;


			var q = from dw in DWs.List.Cast<DW>()
							from d in Enumerable.Range(0, (int)(dw.ToDate - dw.FromDate).TotalDays + 1)
							let date = dw.FromDate.AddDays(d)
							select new dsStruct
							{
								Date = date,
								Customer = dw.Customer,
								Sku = dw.Sku,
								Metric = dw.Metric,
								Value = dw.Value
							};
			var result = q.ToList<dsStruct>();
			return result;

		}
		private void SaveChanges()
		{
			IList<dsStruct> ds = bsPivotGrid.DataSource as IList<dsStruct>;

			var q = ds.OrderBy(c => c.Customer.Oid)
								.ThenBy(c => c.Sku.Oid)
								.ThenBy(c => c.Metric)
								.ThenBy(c => c.Value)

								.Select((s, i) => new { data = s, index = i })
								.GroupBy(k => new
								{
									customer = k.data.Customer,
									sku = k.data.Sku,
									metric = k.data.Metric,
									date = k.data.Date.AddDays(-k.index),
									value = k.data.Value
								},
												d => d.data.Date
												);
			foreach (var g in q)
			{
				DW v = nestedOS.CreateObject<DW>();
				v.Customer = g.Key.customer;
				v.Sku = g.Key.sku;
				v.FromDate = g.Min(d => d.Date);
				v.ToDate = g.Max(d => d.Date);
				v.Metric = g.Key.metric;
				v.Value = g.Key.value;
			}

		}
	}
}
